package com.example.fabric1.util;

import com.example.fabric1.model.channel.Block;
import com.example.fabric1.model.channel.Transaction;
import com.example.fabric1.model.channel.TransactionAction;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.hyperledger.fabric.protos.ledger.rwset.kvrwset.KvRwset;
import org.hyperledger.fabric.sdk.*;
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


@Slf4j
public class ParseUtil {

    public static Block parseBlockInfo(BlockInfo blockInfo) throws InvalidArgumentException, IOException {
        // 新建一个block
        Block block = new Block();
        final long blockNumber = blockInfo.getBlockNumber();
        block.setNumber(blockNumber);
        log.debug("blockNumber = " + blockNumber);
        log.debug("data hash: " + Hex.encodeHexString(blockInfo.getDataHash()));
        log.debug("previous hash id: " + Hex.encodeHexString(blockInfo.getPreviousHash()));
		//log.debug("calculated block hash is " + Hex.encodeHexString(SDKUtils.calculateBlockHash(client, blockNumber, blockInfo.getPreviousHash(), blockInfo.getDataHash())));

        String dataHash = Hex.encodeHexString(blockInfo.getDataHash());
        String previousHash = Hex.encodeHexString(blockInfo.getPreviousHash());
        block.setDataHash(dataHash);
        block.setPreviousHash(previousHash);
        block.setBlockHash(dataHash);

        int txNum = blockInfo.getTransactionCount();
        block.setTxNum(txNum);

        final int envelopeCount = blockInfo.getEnvelopeCount();
        log.debug("block number " + blockNumber + " has " + envelopeCount + " envelope count:");

        List<Transaction> transactionList = new ArrayList<>();

        boolean flag = true;
        for (BlockInfo.EnvelopeInfo info : blockInfo.getEnvelopeInfos()) {

            final String channelId = info.getChannelId();
            log.debug("ChannelId = " + channelId);
            log.debug("TransactionID = " + info.getTransactionID());
            log.debug("ValidationCode = " + info.getValidationCode());
//			log.debug("Timestamp = " + DateUtil.obtain().parseDateFormat(new Date(info.getTimestamp().getTime()), "yyyy年MM月dd日 HH时mm分ss秒"));
            log.debug("Type = " + info.getType());

            Date timestamp = info.getTimestamp();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
            String time = sdf.format(timestamp);
            if (flag) {
                block.setCreateTime(time);
                flag = false;
            }
            List<TransactionAction> transactionActions = new ArrayList<>();
            if (info.getType() == BlockInfo.EnvelopeType.TRANSACTION_ENVELOPE) {
                BlockInfo.TransactionEnvelopeInfo txeInfo = (BlockInfo.TransactionEnvelopeInfo) info;
                int txCount = txeInfo.getTransactionActionInfoCount();
                log.debug("Transaction number " + blockNumber + " has actions count = " + txCount);
                log.debug("Transaction number " + blockNumber + " isValid = " + txeInfo.isValid());
                log.debug("Transaction number " + blockNumber + " validation code = " + txeInfo.getValidationCode());
                // 添加action
                for (int i = 0; i < txCount; i++) {
                    TransactionAction action = new TransactionAction();
                    BlockInfo.TransactionEnvelopeInfo.TransactionActionInfo txInfo = txeInfo.getTransactionActionInfo(i);
                    log.debug("Transaction action " + i + " has response status " + txInfo.getResponseStatus());
                    log.debug("Transaction action " + i + " has response message bytes as string: " + new String(txInfo.getResponseMessageBytes(), "UTF-8"));
                    log.debug("Transaction action " + i + " has endorsements " + txInfo.getEndorsementsCount());
                    String chaincodeIDName = txInfo.getChaincodeIDName();
                    String transactionID = txeInfo.getTransactionID();
                    action.setTxId(transactionID);
                    action.setBlockNum(blockNumber);
                    action.setType(info.getType().toString());
                    action.setChannelId(channelId);
                    action.setTimestamp(time);
                    action.setChaincodeId(chaincodeIDName);

                    List<String> endorsements = new ArrayList<>();
                    for (int n = 0; n < txInfo.getEndorsementsCount(); ++n) {
                        BlockInfo.EndorserInfo endorserInfo = txInfo.getEndorsementInfo(n);
                        log.debug("Endorser " + n + " signature: " + Hex.encodeHexString(endorserInfo.getSignature()));
                        log.debug("Endorser " + n + " endorser: " + new String(endorserInfo.getEndorser(), "UTF-8"));
                        endorsements.add(endorserInfo.getMspid());
                    }

                    action.setEndorsements(endorsements);
                    log.debug("Transaction action " + i + " has " + txInfo.getChaincodeInputArgsCount() + " chaincode input arguments");
                    for (int z = 0; z < txInfo.getChaincodeInputArgsCount(); ++z) {
                        log.debug("Transaction action " + i + " has chaincode input argument " + z + "is: " + new String(txInfo.getChaincodeInputArgs(z), "UTF-8"));
                    }

                    log.debug("Transaction action " + i + " proposal response status: " + txInfo.getProposalResponseStatus());
                    log.debug("Transaction action " + i + " proposal response payload: " + new String(txInfo.getProposalResponsePayload()));

                    TxReadWriteSetInfo rwsetInfo = txInfo.getTxReadWriteSet();
                    List<String> readSetList = new ArrayList<>();
                    List<String> writeSetList = new ArrayList<>();
                    if (null != rwsetInfo) {
                        log.debug("Transaction action " + i + " has " + rwsetInfo.getNsRwsetCount() + " name space read write sets");

                        for (TxReadWriteSetInfo.NsRwsetInfo nsRwsetInfo : rwsetInfo.getNsRwsetInfos()) {
                            final String namespace = nsRwsetInfo.getNamespace();
                            KvRwset.KVRWSet rws = nsRwsetInfo.getRwset();
                            int rs = -1;
                            for (KvRwset.KVRead readList : rws.getReadsList()) {
                                rs++;
                                log.debug("Namespace " + namespace + " read set " + rs + " key " + readList.getKey() + " version [" + readList.getVersion().getBlockNum() + " : " + readList.getVersion().getTxNum() + "]");
                                readSetList.add("Namespace " + namespace + " read set " + rs + " key " + readList.getKey() + " version [" + readList.getVersion().getBlockNum() + " : " + readList.getVersion().getTxNum() + "]");
                            }
                            rs = -1;
                            for (KvRwset.KVWrite writeList : rws.getWritesList()) {
                                rs++;
                                String valAsString = new String(writeList.getValue().toByteArray(), "UTF-8");
                                log.debug("Namespace " + namespace + " write set " + rs + " key " + writeList.getKey() + " has value " + valAsString);
                                writeSetList.add("Namespace " + namespace + " write set " + rs + " key " + writeList.getKey() + " has value " + valAsString);
                            }
                        }
                        action.setWriteSetList(writeSetList);
                        action.setReadSetList(readSetList);
                    }
                    transactionActions.add(action);
                }
            }
            transactionList.add(new Transaction(transactionActions));
        }

        block.setTransactionList(transactionList);
        return block;
    }

    public static Transaction parseTransactionInfo(BlockInfo blockInfo, String txId) throws InvalidArgumentException, IOException {

        final long blockNumber = blockInfo.getBlockNumber();

        List<TransactionAction> transactionActions = new ArrayList<>();

        for (BlockInfo.EnvelopeInfo info : blockInfo.getEnvelopeInfos()) {

            final String channelId = info.getChannelId();

            Date timestamp = info.getTimestamp();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
            String time = sdf.format(timestamp);
            if (info.getTransactionID().equals(txId)) {

                BlockInfo.TransactionEnvelopeInfo txeInfo = (BlockInfo.TransactionEnvelopeInfo) info;
                int txCount = txeInfo.getTransactionActionInfoCount();
                log.debug("Transaction number " + blockNumber + " has actions count = " + txCount);
                log.debug("Transaction number " + blockNumber + " isValid = " + txeInfo.isValid());
                log.debug("Transaction number " + blockNumber + " validation code = " + txeInfo.getValidationCode());
                // 添加action
                for (int i = 0; i < txCount; i++) {
                    TransactionAction action = new TransactionAction();
                    BlockInfo.TransactionEnvelopeInfo.TransactionActionInfo txInfo = txeInfo.getTransactionActionInfo(i);
                    log.debug("Transaction action " + i + " has response status " + txInfo.getResponseStatus());
                    log.debug("Transaction action " + i + " has response message bytes as string: " + new String(txInfo.getResponseMessageBytes(), "UTF-8"));
                    log.debug("Transaction action " + i + " has endorsements " + txInfo.getEndorsementsCount());
                    String chaincodeIDName = txInfo.getChaincodeIDName();
                    String transactionID = txeInfo.getTransactionID();
                    action.setTxId(transactionID);
                    action.setBlockNum(blockNumber);
                    action.setType(info.getType().toString());
                    action.setChannelId(channelId);
                    action.setTimestamp(time);
                    action.setChaincodeId(chaincodeIDName);

                    List<String> endorsements = new ArrayList<>();
                    for (int n = 0; n < txInfo.getEndorsementsCount(); ++n) {
                        BlockInfo.EndorserInfo endorserInfo = txInfo.getEndorsementInfo(n);
                        log.debug("Endorser " + n + " signature: " + Hex.encodeHexString(endorserInfo.getSignature()));
                        log.debug("Endorser " + n + " endorser: " + new String(endorserInfo.getEndorser(), "UTF-8"));
                        endorsements.add(endorserInfo.getMspid());
                    }

                    action.setEndorsements(endorsements);
                    log.debug("Transaction action " + i + " has " + txInfo.getChaincodeInputArgsCount() + " chaincode input arguments");
                    for (int z = 0; z < txInfo.getChaincodeInputArgsCount(); ++z) {
                        log.debug("Transaction action " + i + " has chaincode input argument " + z + "is: " + new String(txInfo.getChaincodeInputArgs(z), "UTF-8"));
                    }

                    log.debug("Transaction action " + i + " proposal response status: " + txInfo.getProposalResponseStatus());
                    log.debug("Transaction action " + i + " proposal response payload: " + new String(txInfo.getProposalResponsePayload()));

                    TxReadWriteSetInfo rwsetInfo = txInfo.getTxReadWriteSet();
                    List<String> readSetList = new ArrayList<>();
                    List<String> writeSetList = new ArrayList<>();
                    if (null != rwsetInfo) {
                        log.debug("Transaction action " + i + " has " + rwsetInfo.getNsRwsetCount() + " name space read write sets");

                        for (TxReadWriteSetInfo.NsRwsetInfo nsRwsetInfo : rwsetInfo.getNsRwsetInfos()) {
                            final String namespace = nsRwsetInfo.getNamespace();
                            KvRwset.KVRWSet rws = nsRwsetInfo.getRwset();
                            int rs = -1;
                            for (KvRwset.KVRead readList : rws.getReadsList()) {
                                rs++;
                                log.debug("Namespace " + namespace + " read set " + rs + " key " + readList.getKey() + " version [" + readList.getVersion().getBlockNum() + " : " + readList.getVersion().getTxNum() + "]");
                                readSetList.add("Namespace " + namespace + " read set " + rs + " key " + readList.getKey() + " version [" + readList.getVersion().getBlockNum() + " : " + readList.getVersion().getTxNum() + "]");
                            }
                            rs = -1;
                            for (KvRwset.KVWrite writeList : rws.getWritesList()) {
                                rs++;
                                String valAsString = new String(writeList.getValue().toByteArray(), "UTF-8");
                                log.debug("Namespace " + namespace + " write set " + rs + " key " + writeList.getKey() + " has value " + valAsString);
                                writeSetList.add("Namespace " + namespace + " write set " + rs + " key " + writeList.getKey() + " has value " + valAsString);
                            }
                        }
                        action.setWriteSetList(writeSetList);
                        action.setReadSetList(readSetList);
                    }
                    transactionActions.add(action);
                }
                return new Transaction(transactionActions);
            }
        }

        return null;
    }
}
